#pragma once
#ifndef __MY_CONNECTION__
#define __MY_CONNECTION__

#include "muduo/net/TcpConnection.h"
#include "muduo/proto/codec.h"
#include "./virtual_host.hpp"
#include "./consumer.hpp"
#include "./route.hpp"
#include "../mqcommon/thread_pool.hpp"
#include "./channel.hpp"
#include <iostream>
#include <memory>
#include <string>

/**
 * 信道所依赖的就是通信连接，但是muduo库中的连接无法满足我们的的需求，所以我们需要再加一层中间层
 * 创建我们自己的连接结构，来建立muduo库中连接与信道的联系
 * 而我们自己设计的连接中，要有着基础的muduo库连接，以及建立信道时信道所需要的功能句柄以及资源句柄
 *
 */

namespace NS_Connection
{
    class Connection
    {
    public:
        using Ptr = std::shared_ptr<Connection>;
        Connection(const NS_Host::VirtualHost::Ptr &host,
                   const NS_Consumer::ConsumerManager::Ptr &cmp,
                   const NS_Channel::ProtobufCodecPtr &codec,
                   const muduo::net::TcpConnectionPtr &conn,
                   const ThreadPoolPtr &thread_pool)
            : _virtual_host(host), _consumer_manager(cmp), _conn(conn), _codec(codec),
              _thread_pool(thread_pool), _channel_manager(std::make_shared<NS_Channel::ChannelManager>()) // 要注意在创建连接的时候，也需要实例化信道的管理模块，这个资源是由连接来承担的
        {
        }

        void openChannel(const NS_Channel::OpenChannelRequestPtr &req)
        {
            int ret = _channel_manager->openChannel(req->channel_id(), _virtual_host, _consumer_manager, _codec, _conn, _thread_pool);
            return basicResponse(req->request_id(), req->channel_id(), ret);
        }
        void closeChannel(const NS_Channel::CloseChannelRequestPtr &req)
        {
            _channel_manager->closeChannel(req->channel_id());
            return basicResponse(req->request_id(), req->channel_id(), true);
        }
        NS_Channel::Channel::Ptr getChannel(const std::string &channel_id)
        {
            return _channel_manager->getChannel(channel_id);
        }

        Connection() {}
        ~Connection()
        {
            
        }

    private:
        // 向指定信道发送响应状态
        void basicResponse(const std::string &request_id, const std::string &channel_id, bool ok)
        {
            NS_Protocol::BasicCommonResponse resp;
            resp.set_request_id(request_id);
            resp.set_channel_id(channel_id);
            resp.set_ok(ok);
            _codec->send(_conn, resp);
        }

    private:
        NS_Channel::ChannelManager::Ptr _channel_manager;
        muduo::net::TcpConnectionPtr _conn;
        NS_Channel::ProtobufCodecPtr _codec;
        NS_Consumer::ConsumerManager::Ptr _consumer_manager;
        NS_Host::VirtualHost::Ptr _virtual_host;
        ThreadPoolPtr _thread_pool;
    };

    class ConnectionManager
    {
    public:
        using Ptr = std::shared_ptr<ConnectionManager>;

        ConnectionManager() {}

        void newConnection(const NS_Host::VirtualHost::Ptr &host,
                           const NS_Consumer::ConsumerManager::Ptr &cmp,
                           const NS_Channel::ProtobufCodecPtr &codec,
                           const muduo::net::TcpConnectionPtr &conn,
                           const ThreadPoolPtr &thread_pool)
        {
            std::unique_lock<std::mutex> ul(_mtx);
            if (_connections.find(conn) != _connections.end())
                return;

            _connections.insert(std::make_pair(conn, std::make_shared<Connection>(host, cmp, codec, conn, thread_pool)));
        }
        void delConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            std::unique_lock<std::mutex> ul(_mtx);
            _connections.erase(conn);
        }

        Connection::Ptr getConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            std::unique_lock<std::mutex> ul(_mtx);
            if (_connections.find(conn) == _connections.end())
                return Connection::Ptr();

            return _connections[conn];
        }

        ~ConnectionManager() {}

    private:
        std::mutex _mtx;
        std::unordered_map<muduo::net::TcpConnectionPtr, Connection::Ptr> _connections;
    };
}

#endif